﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Directory = Lucene.Net.Store.Directory;
using Version = Lucene.Net.Util.Version;
using NewsMine.Utilities;
using Lucene.Net.QueryParsers;

namespace LuceneWrap.Lib
{
    public interface ILuceneManager<T>
    {
        void Create();
        void AddItemToIndex(T obj);
        void AddItemsToIndex(List<T> objects);
        void AddObjectToIndex(T obj);
        void ModifyItemFromIndex(T oldObj, T newObj);
        void ModifyItemFromIndex(List<T> oldObj, List<T> newObj);
        void DeleteObjectFromIndex(T oldObj);
        void InsertUpdateFieldFromIndex(T newfield);
        void FinalizeWriter(bool optimize);
        bool IsIndexLocked(string folderPath);
    }

    public class LuceneManager<T> : ILuceneManager<T>
    {
        private readonly string _INDEX_FILEPATH = ConfigurationManager.AppSettings.Get("LuceneIndexFilePath");


        private Analyzer _analyzer = null;
        private IndexWriter _indexWriter = null;
        private IndexReader _indexReader = null;
        private Directory _luceneIndexDirectory = null;

        public IndexWriter IndexWriter
        {
            get
            {
                return _indexWriter;
            }
        }

        //public LuceneManager()
        //{
        //}

        public LuceneManager(string indexFilePath)
        {
            _INDEX_FILEPATH = indexFilePath;
            Create();
        }

        public void Create()
        {
            _analyzer = new WhitespaceAnalyzer();// (Version.LUCENE_29);
            _luceneIndexDirectory = FSDirectory.Open(new DirectoryInfo(_INDEX_FILEPATH));
            _indexWriter = new IndexWriter(_luceneIndexDirectory, _analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
            _indexReader = IndexReader.Open(_luceneIndexDirectory, false);
            _indexReader.Close();
        }

        #region Insert index

        #region Public methods

        public void AddItemToIndex(T obj)
        {
            AddObjectToIndex(obj);
        }

        public void AddItemsToIndex(List<T> objects)
        {
            foreach (var obj in objects)
            {
                AddObjectToIndex(obj);
            }
        }

        public void AddDocumentsToIndex(List<Document> docs)
        {
            foreach (var doc in docs)
            {
                _indexWriter.AddDocument(doc);
            }
        }

        #endregion

        #region Private methods

        public void AddObjectToIndex(T obj)
        {
            Document document = new Document();


            Field entireObjectToPersist = new Field(CommonConstants.ObjectBinaryFieldName, ObjectByteArrayConverter.ObjectToByteArray(obj), Field.Store.YES);
            document.Add(entireObjectToPersist);

            Field clrTypeField = new Field(CommonConstants.ClrObjectTypeName, obj.GetType().FullName.ToLower(), Field.Store.NO, Field.Index.NOT_ANALYZED);
            document.Add(clrTypeField);

            var newFields = FeedLuceneReflection.GetLuceneFields(obj, false);

            foreach (var newField in newFields)
            {
                document.Add(newField);
            }


            _indexWriter.AddDocument(document);
        }

        #endregion

        #endregion

        #region UpdateIndex

        #region Public methods

        public void ModifyItemFromIndex(T oldObj, T newObj)
        {
            DeleteObjectFromIndex(oldObj);
            InsertUpdateFieldFromIndex(newObj);
        }

        public void ModifyItemFromIndex(List<T> oldObj, List<T> newObj)
        {
            foreach (var field in oldObj)
            {
                DeleteObjectFromIndex(field);
            }
            foreach (var field in newObj)
            {
                InsertUpdateFieldFromIndex(field);
            }
        }

        #endregion

        #region Private methods

        public void DeleteObjectFromIndex(T oldObj)
        {
            var oldFields = FeedLuceneReflection.GetLuceneFields(oldObj, false);
            foreach (var oldField in oldFields)
            {
                _indexWriter.DeleteDocuments(new Term(oldField.Name(), oldField.StringValue()));
            }
        }

        /// <summary>
        /// This is limited to the string type fields, range will not work here.
        /// 
        /// the key should be more procice, like id=123
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="feledValue"></param>
        public void DeleteObjectFromIndexByField(string fieldName, string feledValue)
        {
            _indexWriter.DeleteDocuments(new Term(fieldName, feledValue));
        }

        private QueryParser _queryParser = null;

        public void DeleteObjectFromIndexByQuery(string queryText)
        {
            if(_queryParser==null)
            _queryParser = new QueryParser("id", _analyzer);

            Query query = _queryParser.Parse(queryText); 

            _indexWriter.DeleteDocuments(query);
        }


        public void InsertUpdateFieldFromIndex(T newfield)
        {
            AddObjectToIndex(newfield);
        }

        #endregion

        #endregion

        public void FinalizeWriter(bool optimize)
        {
            if (optimize)
                _indexWriter.Optimize();
            _indexWriter.Commit();
            _indexWriter.Close();
            _luceneIndexDirectory.Close();
        }



        public bool IsIndexLocked(string folderPath)
        {
            return IndexWriter.IsLocked(folderPath);
        }
    }


}
